/*
 * 
 *  This file is part of Sourceprojects.org Lycia Copyright 2010 noctarius
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 * 
 */
package org.sourceprojects.lycia.internal.dom;

import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.sourceprojects.lycia.NamespaceResolver;
import org.sourceprojects.lycia.ResourceResolver;
import org.sourceprojects.lycia.exceptions.LyciaInitializationException;
import org.sourceprojects.lycia.internal.logging.LyciaLoggerWrapper;
import org.sourceprojects.lycia.internal.resolver.BasicNamespaceResolver;
import org.sourceprojects.lycia.internal.resolver.BasicNamespaceResolver.NamespaceResolverHandlerContainer;

/**
 * 
 * @author noctarius
 * @since 0.0.1
 */
public class ResourceResolverImpl implements ResourceResolver {
	private static final LyciaLoggerWrapper LOGGER = LyciaLoggerWrapper
			.getLogger(ResourceResolverImpl.class);

	private final Map<String, NamespaceResolver> namespaceResolvers = new HashMap<String, NamespaceResolver>();
	private final BasicNamespaceResolver basicNamespaceResolver;
	private boolean showWarning = true;

	public ResourceResolverImpl() {
		try {
			final List<NamespaceResolverHandlerContainer> containers = new ArrayList<NamespaceResolverHandlerContainer>();

			final ClassLoader cl = Thread.currentThread()
					.getContextClassLoader();

			final Enumeration<URL> xmlparserResources = cl
					.getResources("META-INF/xmlparser/schema.properties");
			readRedirects(containers, xmlparserResources);

			final Enumeration<URL> lyciaResources = cl
					.getResources("META-INF/lycia/schema.properties");
			readRedirects(containers, lyciaResources);

			final NamespaceResolverHandlerContainer[] handler = new NamespaceResolverHandlerContainer[containers
					.size()];
			containers.toArray(handler);
			basicNamespaceResolver = new BasicNamespaceResolver(handler, cl);
		} catch (final IOException e) {
			throw new LyciaInitializationException(
					"Failed internal initialization", e);
		}
	}

	private void readRedirects(
			final List<NamespaceResolverHandlerContainer> containers,
			final Enumeration<URL> resources) throws IOException {

		if (resources == null) {
			return;
		}

		while (resources.hasMoreElements()) {
			final URL url = resources.nextElement();

			LOGGER.info("Found schema.properties in '" + url.toString() + "'");

			final Properties properties = new Properties();
			properties.load(url.openStream());

			final Enumeration<Object> keys = properties.keys();
			while (keys.hasMoreElements()) {
				final String key = (String) keys.nextElement();
				final String value = properties.getProperty(key);

				containers
						.add(new NamespaceResolverHandlerContainer(key, value));
			}
		}
	}

	@Override
	public boolean isShowWarning() {
		return showWarning;
	}

	@Override
	public void setShowWarning(final boolean showWarning) {
		this.showWarning = showWarning;
	}

	@Override
	public void registerNamespaceResolver(
			final NamespaceResolver namespaceResolver) {
		final String namespaceURI = namespaceResolver.getNamespaceURI();

		if (namespaceResolvers.get(namespaceURI) != null && isShowWarning()) {
			final StringBuilder sb = new StringBuilder();
			sb.append("Namespace ").append(namespaceURI).append(
					" is already defined and will be overridden.");
			LOGGER.warn(sb.toString());
		}

		namespaceResolvers.put(namespaceURI, namespaceResolver);
	}

	@Override
	public void unregisterNamespaceResolver(
			final NamespaceResolver namespaceResolver) {
		unregisterNamespaceResolver(namespaceResolver.getNamespaceURI());
	}

	@Override
	public void unregisterNamespaceResolver(final String namespaceURI) {
		namespaceResolvers.remove(namespaceURI);
	}

	@Override
	public ResourceResolverResult resolveResource(final String type,
			final String namespaceURI, final String publicId,
			final String systemId, final String baseURI) {

		final NamespaceResolver resolver = namespaceResolvers.get(namespaceURI);
		if (resolver != null) {
			if (LOGGER.isDebugEnabled()) {
				LOGGER.debug("Found NamespaceResolver for namespace '"
						+ namespaceURI + "'");
			}

			return resolver.getInput(type, namespaceURI, publicId, systemId,
					baseURI);
		} else {
			if (LOGGER.isDebugEnabled()) {
				LOGGER.debug("Using BasicNamespaceResolver for namespace '"
						+ namespaceURI + "'");
			}

			return basicNamespaceResolver.getInput(type, namespaceURI,
					publicId, systemId, baseURI);
		}
	}

	@Override
	public String toString() {
		return "ResourceResolverImpl [basicNamespaceResolver="
				+ basicNamespaceResolver + ", namespaceResolvers="
				+ namespaceResolvers + ", showWarning=" + showWarning + "]";
	}

}
